#include "stdafx.h"

#include <MyControls/SegmentControl.h>
#include <MyControls/SegmentList.h>
#include <MyControls/Timeline.h>

#include <math/round.h>
#include <sstream>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

// LeftBracket

CRect SegmentControl::LeftBracket::Bounds() const
{
	CSize s = m_List.m_Left.Size();
	CRect r(0, 0, s.cx, s.cy);
	if (!m_Control.IsEmpty())
	{
		int x = m_List.TimeToClient(m_Control.GetTimeSpan().first);
		int mx = m_List.TimeToClient(m_Control.GetOrigin()) - m_List.m_Mask.Width() / 2; 
		if (x > mx)
			x = mx;
		r.OffsetRect(x - s.cx + 1, 1);
	}
	return r;
}

void SegmentControl::LeftBracket::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	Control::HandleLeftDown(flags, p);
	if (!m_Control.IsEmpty())
		m_List.GetSegments()->SetActiveSegment(m_Control.m_Index);
}

bool SegmentControl::LeftBracket::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (m_Control.IsEmpty())
		return false;
	if (m_Control.GetSegment().IsProcessed())
		return false;
	m_StartPos = ClientToParent(p0).x;
	m_StartValue = m_List.TimeToClient(m_Control.GetTimeSpan().first);
	return true;
}

void SegmentControl::LeftBracket::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_Timeline.GetSequence()->SetPreviewFrame(m_Timeline.GetCurrentFrame());
	m_Control.UpdateTimeSpan();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void SegmentControl::LeftBracket::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + ClientToParent(p).x - m_StartPos;
	int t = round(m_List.ClientToTime(x));
	if (t > m_Control.GetOrigin())
		t = m_Control.GetOrigin();
	Span s = m_Control.GetTimeSpan();
	s.first = t;
	m_Control.SetTimeSpan(s);
	m_Timeline.GetSequence()->SetPreviewFrame(m_Control.GetTimeSpan().first);

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	int gap = m_List.m_Gap;
	if (x < gap)
		m_Timeline.SetTimeAutoScroll(x - gap);
	else if (x > m_List.Width() - gap)
		m_Timeline.SetTimeAutoScroll(x - m_List.Width() + gap);
	else
		m_Timeline.SetTimeAutoScroll(0);
}

// RightBracket

CRect SegmentControl::RightBracket::Bounds() const
{
	CSize s = m_List.m_Right.Size();
	CRect r(0, 0, s.cx, s.cy);
	if (!m_Control.IsEmpty())
	{
		int x = m_List.TimeToClient(m_Control.GetTimeSpan().last);
		int mx = m_List.TimeToClient(m_Control.GetOrigin()) - m_List.m_Mask.Width() / 2 + m_List.m_Mask.Width(); 
		if (x < mx)
			x = mx;
		r.OffsetRect(x, 1);
	}
	return r;
}

void SegmentControl::RightBracket::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	Control::HandleLeftDown(flags, p);
	if (!m_Control.IsEmpty())
		m_List.GetSegments()->SetActiveSegment(m_Control.m_Index);
}

bool SegmentControl::RightBracket::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (m_Control.IsEmpty())
		return false;
	if (m_Control.GetSegment().IsProcessed())
		return false;
	m_StartPos = ClientToParent(p0).x;
	m_StartValue = m_List.TimeToClient(m_Control.GetTimeSpan().last);
	return true;
}

void SegmentControl::RightBracket::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_Timeline.GetSequence()->SetPreviewFrame(m_Timeline.GetCurrentFrame());
	m_Control.UpdateTimeSpan();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void SegmentControl::RightBracket::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + ClientToParent(p).x - m_StartPos;
	int t = round(m_List.ClientToTime(x));
	if (t < m_Control.GetOrigin() + 1)
		t = m_Control.GetOrigin() + 1;
	Span s = m_Control.GetTimeSpan();
	s.last = t;
	m_Control.SetTimeSpan(s);
	m_Timeline.GetSequence()->SetPreviewFrame(m_Control.GetTimeSpan().last);

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	int gap = m_List.m_Gap;
	if (x < gap)
		m_Timeline.SetTimeAutoScroll(x - gap);
	else if (x > m_List.Width() - gap)
		m_Timeline.SetTimeAutoScroll(x - m_List.Width() + gap);
	else
		m_Timeline.SetTimeAutoScroll(0);
}

// SegmentControl

SegmentControl::SegmentControl(SegmentList & graph) : 
	m_List(graph), m_Timeline(graph.m_Timeline),
	m_LeftBracket(* this),
	m_RightBracket(* this)
{
	m_Index = -1;
	m_pSegment = 0;
	m_AnimatedIndex = 0;
	m_BrushOrg = 0;
	m_List.Add(this);
}

SegmentControl::~SegmentControl()
{
}

String SegmentControl::Hint() const
{
	std::ostringstream out;
	out << "Segment";
	if (!IsEmpty()) {
		Span s = GetSegment().GetTimeSpan();
		out << " [" << s.first << "-" << s.last << "]";
	}
	return out.str();
}

Segment & SegmentControl::GetSegment() const
{
	assert(m_pSegment != 0);
	return * m_pSegment;
}

void SegmentControl::SetSegmentIndex(int index)
{
	if (m_pSegment != 0) 
	{
		m_pSegment->Detach(this);
	}
	assert(index >= -1 &&
		index < int(m_List.GetSegments()->GetSegmentCount()));
	m_Index = index;
	if (m_Index != -1) {
		m_pSegment = &m_List.GetSegments()->GetSegment(m_Index);
		m_pSegment->Attach(this);
		SetTimeSpan(m_pSegment->GetTimeSpan());
	} else {
		m_pSegment = 0;
	}
}

bool SegmentControl::IsEmpty() const
{
	return m_List.GetSegments()->IsEmpty() || m_Index == -1;
}


CRect SegmentControl::Bounds() const
{
	if (IsEmpty())
		return CRect(0, 0, 0, 0);
	CRect r;
	r.left = 0;
	r.right = m_List.Width();
	r.top = RowHeight + DividerHeight + 2 + m_Index * RowHeight - m_List.GetVOffset();
	r.bottom = r.top + RowHeight;
	return r;
}

void SegmentControl::ProcessEvent(My::Event & e)
{
	if (e.Is<Segment::TimeSpanChange>())
		OnTimeSpanChanged();
	else if (e.Is<Segment::DataSpanChange>())
		OnDataSpanChanged();
	else if (e.Is<Segment::StateChange>())
		OnStatusChanged();
	else
		Container::ProcessEvent(e);
}

void SegmentControl::Paint(CDC * pDC, const CRect & rect)
{
	if (IsEmpty())
		return;

	CRect rClient = ClientRect();

	CBrush bFace; bFace.CreateSysColorBrush(COLOR_BTNFACE);
	CBrush bBlack(RGB(0, 0, 0));
	CBrush bLight; bLight.CreateSysColorBrush(COLOR_BTNHILIGHT);
	CBrush bHalfLight(MeanSysColor(COLOR_BTNFACE, COLOR_BTNHILIGHT));
	CBrush bHalfShadow(MeanSysColor(COLOR_BTNFACE, COLOR_BTNSHADOW));
	CBrush bShadow; bShadow.CreateSysColorBrush(COLOR_BTNSHADOW);

	Span s = GetSegment().GetDataSpan();
	CRect rDataSpan;
	rDataSpan.top = rClient.top;
	rDataSpan.left = m_List.TimeToClient(s.first); 
	rDataSpan.right = m_List.TimeToClient(s.last) + 1;
	rDataSpan.bottom = rClient.bottom - 1;

	pDC->FillRect(&rDataSpan, &bHalfLight);
	pDC->FrameRect(&rDataSpan, &bShadow);

	s = GetTimeSpan();

	CRect rTimeSpan;
	rTimeSpan.top = rClient.top;
	rTimeSpan.left = m_List.TimeToClient(s.first); 
	rTimeSpan.right = m_List.TimeToClient(s.last) + 1;
	rTimeSpan.bottom = rClient.bottom - 1;

	CBrush bGround;
	CBrush bData;

	if (!GetSegment().IsProcessed()) {

		if (m_List.GetSegments()->GetActiveSegment() == m_Index) {
			bGround.CreatePatternBrush(&m_List.m_LightOrangeRipple);
			bData.CreatePatternBrush(&m_List.m_DarkOrangeRipple);
		} else {
			bGround.CreateSolidBrush(RGB(255, 240, 192));
			bData.CreateSolidBrush(RGB(255, 192, 0));
		}

	} else {

		if (m_List.GetSegments()->GetActiveSegment() == m_Index) {
			bGround.CreatePatternBrush(&m_List.m_LightBlueRipple);
			bData.CreatePatternBrush(&m_List.m_DarkBlueRipple);
		} else {
			bGround.CreateSolidBrush(RGB(224, 224, 255));
			bData.CreateSolidBrush(RGB(192, 192, 255));
		}

	}

	pDC->SetBrushOrg(GetWindowOffset() + CPoint(m_List.TimeToClient(GetOrigin()), 0) + m_BrushOrg);

	pDC->FillRect(&rTimeSpan, &bGround);
	rDataSpan.IntersectRect(&rDataSpan, &rTimeSpan);
	pDC->FillRect(&rDataSpan, &bData);

	CBrush bFrame;
	if (m_List.GetSegments()->GetActiveSegment() == m_Index && HasFocus())
		bFrame.CreatePatternBrush(&m_List.m_Alternate);
	else
		bFrame.CreateSysColorBrush(COLOR_BTNSHADOW);

	pDC->FrameRect(&rTimeSpan, &bFrame);

	pDC->SetBrushOrg(0, 0);

	CRect rMask(rClient);
	rMask.left = m_List.TimeToClient(GetOrigin()) - m_List.m_Mask.Width() / 2;
	rMask.right = rMask.left + m_List.m_Mask.Width();

	if (GetSegment().IsProcessed())
		m_List.m_MaskGrayed.Draw(pDC, rMask.left, 0);
	else {
		if (GetSegment().IsMaskEditing())
			m_List.m_MaskPressed.Draw(pDC, rMask.left, 0);
		else
			m_List.m_Mask.Draw(pDC, rMask.left, 0);
	}

	if (rTimeSpan.left > rMask.left)
		rTimeSpan.left = rMask.left;
	if (rTimeSpan.right < rMask.right)
		rTimeSpan.right = rMask.right;

	m_List.m_Left.Draw(pDC, rTimeSpan.left - m_List.m_Left.Width() + 1, 0);
	m_List.m_Right.Draw(pDC, rTimeSpan.right - 1, 0);
	
//	pDC->SelectClipRgn(0);
}

void SegmentControl::HandleKeyDown(unsigned int key, unsigned int count, unsigned int flags)
{
	if (IsEmpty())
		return;
	if (key == VK_RETURN) {
		GetSegment().StartProcessing();
		return;
	}
	if (key == VK_ESCAPE) {
		GetSegment().CancelProcessing(false);
		return;
	}
}

void SegmentControl::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	My::Control::HandleLeftDown(flags, p);
	if (IsEmpty())
		return;
	CRect rSpan = GetUnionSpanRect();
	if (rSpan.PtInRect(p)) {
		CRect rMask(rSpan);
		rMask.left = m_List.TimeToClient(GetOrigin()) - m_List.m_Mask.Width() / 2;
		rMask.right = rMask.left + m_List.m_Mask.Width();
		if (rMask.PtInRect(p) && !GetSegment().IsProcessed()) {
			if (GetSegment().IsMaskEditing())
				GetSegment().CloseMaskEditor();
			else {
				m_List.GetSegments()->SetActiveSegment(m_Index);
				m_Timeline.GetSequence()->SetCurrentFrame(GetOrigin());
				GetSegment().OpenMaskEditor();
			}
		} else {
			m_List.GetSegments()->SetActiveSegment(m_Index);
		}
	} else {
		m_List.GetSegments()->SetActiveSegment(-1);
	}
}

void SegmentControl::HandleLeftDoubleClick(unsigned int flags, const CPoint &p)
{
	My::Control::HandleLeftDoubleClick(flags, p);
	if (IsEmpty())
		return;
	if (GetUnionSpanRect().PtInRect(p)) {
		if (GetSegment().IsProcessed())
			GetSegment().CancelProcessing(true);
		else
			GetSegment().StartProcessing();
	}
}

bool SegmentControl::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (IsEmpty())
		return false;
	Span d = GetSegment().GetDataSpan();
	Span s = GetTimeSpan();
	if (s.first > d.first)
		s.first = d.first;
	if (s.last < d.last)
		s.last = d.last;
	CRect rSpan(ClientRect());
	rSpan.left = m_List.TimeToClient(s.first) - m_List.m_Left.Width(); 
	rSpan.right = m_List.TimeToClient(s.last) + m_List.m_Right.Width();
	return rSpan.PtInRect(p) != 0;
}

void SegmentControl::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_List.HideInsertion();
	m_List.m_ShowInsertion = false;
	m_List.m_InsertionSource = -1;

	int y = ClientToParent(p).y;
	int pos = round(double(y - RowHeight - DividerHeight - 2 +  m_List.GetVOffset()) / RowHeight);
	if (pos < 0)
		pos = 0;
	if (pos > int(m_List.GetSegments()->GetSegmentCount()))
		pos = m_List.GetSegments()->GetSegmentCount();

	m_List.GetSegments()->MoveSelection(pos);
	m_List.OnAutoScroll(false);
	m_List.SetVAutoScroll(0);
}

void SegmentControl::HandleDragging(unsigned int flags, const CPoint & p)
{
	m_List.m_ShowInsertion = true;
	m_List.m_InsertionSource = m_Index;
	m_List.ShowInsertion();

	int y = ClientToParent(p).y;
	int pos = round(double(y - RowHeight - DividerHeight - 2 +  m_List.GetVOffset()) / RowHeight);
	if (pos < 0)
		pos = 0;
	if (pos > int(m_List.GetSegments()->GetSegmentCount()))
		pos = m_List.GetSegments()->GetSegmentCount();

	m_List.Insertion(pos);

	// Autoscroll?
	if (y < RowHeight + DividerHeight + m_List.m_Gap)
		m_List.SetVAutoScroll(y - RowHeight - DividerHeight - m_List.m_Gap);
	else if (y > m_List.Height() - m_List.m_Gap)
		m_List.SetVAutoScroll(y - m_List.Height() + m_List.m_Gap);
	else
		m_List.SetVAutoScroll(0);
}

void SegmentControl::HandleFocus()
{
	Invalidate();
}

void SegmentControl::HandleUnfocus()
{
	Invalidate();
}

void SegmentControl::StartAnimation()
{
	if (m_AnimatedIndex != 0)
		return;

	m_AnimatedIndex = m_List.m_Animated.insert(m_List.m_Animated.end(), this);
	m_List.UpdateAnimation();
}

void SegmentControl::StopAnimation()
{
	if (m_AnimatedIndex == 0)
		return;

	m_List.m_Animated.erase(m_AnimatedIndex);
	m_AnimatedIndex = 0;
	m_List.UpdateAnimation();
}

void SegmentControl::Animate()
{
	m_BrushOrg.x = (m_BrushOrg.x + 1) % 63;
	m_BrushOrg.y = (m_BrushOrg.y + 1) % 63;
	Invalidate();
}

Span SegmentControl::GetTimeSpan() const
{
	return m_TimeSpan;
}

void SegmentControl::SetTimeSpan(Span s)
{
	s.Intersect(m_Timeline.GetSequence()->GetTimeSpan());
	if (s != m_TimeSpan) {
		m_TimeSpan = s;
		Invalidate();
	}
}

void SegmentControl::UpdateTimeSpan()
{
	if (!IsEmpty())
		GetSegment().SetTimeSpan(GetTimeSpan());
}

int SegmentControl::GetOrigin() const
{
	return IsEmpty() ? 0 : GetSegment().GetOrigin();
}

CRect SegmentControl::GetUnionSpanRect() const
{
	Span s = GetSegment().GetDataSpan().Unite(GetTimeSpan());
	CRect rSpan(ClientRect());
	rSpan.left = m_List.TimeToClient(s.first) - m_List.m_Left.Width(); 
	rSpan.right = m_List.TimeToClient(s.last) + m_List.m_Right.Width();
	return rSpan;
}

void SegmentControl::OnInitialUpdate()
{
	SetTimeSpan(GetSegment().GetTimeSpan());
}

void SegmentControl::OnTimeSpanChanged()
{
	if (!IsEmpty())
		SetTimeSpan(GetSegment().GetTimeSpan());
}

void SegmentControl::OnDataSpanChanged()
{
	Invalidate();
}

void SegmentControl::OnStatusChanged()
{
	Invalidate();
}

} // My